﻿namespace SharpF2.ModelView
{
	using System;
	using System.Collections.ObjectModel;
	using System.ComponentModel;
	using System.Globalization;
	using System.IO;
	using System.Text;
	using System.Windows;
	using Model;
	using Properties;

	/// <summary>
	/// Define an identifier in the regex match pattern.
	/// </summary>
	[Serializable]
	public class IdentifierItem : INotifyPropertyChanged, IComparable

	{
		#region Events

		/// <summary>
		/// Event raised when a property has changed.
		/// </summary>
		public event PropertyChangedEventHandler PropertyChanged;

		#endregion

		#region Constructors

		public IdentifierItem()
		{
			CaseFilter = CaseFilterEnums.NoChange;
			ReplaceFilters = new ObservableCollection<ReplaceFilter>();
			IdentifierId = String.Empty;
			IsAnInteger = false;
			IntegerLength = 0;
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="IdentifierItem" /> class. 
		/// Default constructor.
		/// </summary>
		/// <param name="identifierId">Id of the IdentifierItem.</param>
		public IdentifierItem(string identifierId)
		{
			CaseFilter = CaseFilterEnums.NoChange;
			ReplaceFilters = new ObservableCollection<ReplaceFilter>();
			IdentifierId = identifierId;
			IsAnInteger = false;
			IntegerLength = 0;
		}

		public IdentifierItem(IdentifierItem item)
		{
			CaseFilter = item.CaseFilter;
			ReplaceFilters = new ObservableCollection<ReplaceFilter>(item.ReplaceFilters);
			IdentifierId = item.IdentifierId;
			IsAnInteger = item.IsAnInteger;
			IntegerLength = item.IntegerLength;
		}

		#endregion

		#region IComparable Members

		public int CompareTo(object obj)
		{
			var identifierItemB = obj as IdentifierItem;
			return null != identifierItemB ? IdentifierId.CompareTo(identifierItemB.IdentifierId) : 0;
		}

		#endregion

		#region Private Attributes

		/// <summary>
		/// The identifier case filter.
		/// </summary>
		private CaseFilterEnums caseFilter;

		/// <summary>
		/// The lenght of the integer if the identifier is an integer.
		/// </summary>
		private UInt32 integerLength;

		/// <summary>
		/// True if the identifier is an integer, else false.
		/// </summary>
		private Boolean isAnInteger;

		#endregion

		#region Public Properties

		/// <summary>
		/// Gets or sets CaseFilter.
		/// </summary>
		public CaseFilterEnums CaseFilter
		{
			get { return caseFilter; }

			set
			{
				caseFilter = value;
				NotifyPropertyChanged("CaseFilter");
			}
		}

		/// <summary>
		/// Gets the Id of the identifier.
		/// </summary>
		public String IdentifierId { get; set; }

		/// <summary>
		/// Gets or sets IntegerLength.
		/// </summary>
		public UInt32 IntegerLength
		{
			get { return integerLength; }

			set
			{
				integerLength = value <= 15 ? value : 15;
				NotifyPropertyChanged("IntegerLength");
			}
		}

		/// <summary>
		/// Gets or sets a value indicating whether IsAnInteger.
		/// </summary>
		public Boolean IsAnInteger
		{
			get { return isAnInteger; }

			set
			{
				isAnInteger = value;
				NotifyPropertyChanged("IsAnInteger");
			}
		}

		/// <summary>
		/// Gets a collection of ReplaceFilter to apply to this identifier.
		/// </summary>
		public ObservableCollection<ReplaceFilter> ReplaceFilters { get; set; }

		#endregion

		#region Public Members

		public static IdentifierItem GetIdentifierItem(String identifierId)
		{
			IdentifierItem item;
			Load(Settings.Default.DefaultIdentifierItem, out item);
			item.IdentifierId = identifierId;
			return item;
		}

		public static void SetIdentifierItem(IdentifierItem item)
		{
			String xml;
			Save(item, out xml);
			Settings.Default.DefaultIdentifierItem = xml;
			Settings.Default.Save();
			Settings.Default.Reload();
		}

		/// <summary>
		/// Add a ReplaceFilter.
		/// </summary>
		/// <param name="filter">The ReplaceFilter to add.</param>
		public void AddReplaceFilter(ReplaceFilter filter)
		{
			if (null == filter)
				return;

			if (ReplaceFilters.Contains(filter)) return;

			ReplaceFilters.Add(filter);
			filter.PropertyChanged += Filter_PropertyChanged;

			NotifyPropertyChanged("SelectedIdentifiersReplaceFilters");
		}

		/// <summary>
		/// Filter an input corresponding to the identifier with the IsAnInteger,
		/// SelectedIdentifiersReplaceFilters and selected CaseFilter.
		/// </summary>
		/// <param name="input">The input corresponding to the identifier.</param>
		/// <returns>
		/// The formatted input corresponding to the identifier.
		/// </returns>
		public String Filter(String input)
		{
			var output = input;

			if (IsAnInteger)
			{
				Int32 integer;
				if (Int32.TryParse(input, NumberStyles.None, CultureInfo.CurrentCulture, out integer))
				{
					var number = integer.ToString();
					if (IntegerLength - number.Length > 0)
					{
						var numberBase = new string('0', (Int32) (IntegerLength - number.Length));
						output = numberBase + number;
					}
					else
					{
						output = number;
					}
				}
			}

			foreach (var filter in ReplaceFilters)
			{
				output = filter.Filter(output);
			}

			switch (CaseFilter)
			{
				case CaseFilterEnums.NoChange:
					break;
				case CaseFilterEnums.LowerCase:
					output = output.ToLowerInvariant();
					break;
				case CaseFilterEnums.UpperCase:
					output = output.ToUpperInvariant();
					break;
				case CaseFilterEnums.FirstLetterUpperCase:
					output = StringFilter.FirstLetterUpperCase(output);
					break;
				case CaseFilterEnums.CapitalizeWords:
					output = StringFilter.CapitalizeWords(output);
					break;
				default:
					throw new ArgumentOutOfRangeException();
			}

			return output;
		}

		/// <summary>
		/// Remove a ReplaceFilter.
		/// </summary>
		/// <param name="filter">The ReplaceFilter to remove.</param>
		public void RemoveReplaceFilter(ReplaceFilter filter)
		{
			ReplaceFilters.Remove(filter);

			NotifyPropertyChanged("SelectedIdentifiersReplaceFilters");
		}

		#endregion

		#region Private Members

		private static void Load(String xml, out IdentifierItem item)
		{
			try
			{
				using (var sr = new StringReader(xml))
				{
					item = new IdentifierItem();
					var currentPattern = String.Empty;

					String line;
					while ((line = sr.ReadLine()) != null)
					{
						var seperatorIndex = line.IndexOf(':');
						if (seperatorIndex < 0)
						{
							item = new IdentifierItem();
							return;
						}

						var identifier = line.Substring(0, seperatorIndex);
						var value = line.Substring(seperatorIndex + 1);


						switch (identifier)
						{
							case "CaseFilter":
								{
									item.caseFilter = (CaseFilterEnums) Enum.Parse(typeof (CaseFilterEnums), value);
									break;
								}
							case "IsAnInteger":
								{
									item.IsAnInteger = Boolean.Parse(value);
									break;
								}
							case "IntegerLength":
								{
									item.IntegerLength = UInt32.Parse(value, NumberStyles.None);
									break;
								}
							case "ReplaceFilter.Pattern":
								{
									currentPattern = value;
									break;
								}
							case "ReplaceFilter.Replacement":
								{
									if (String.IsNullOrEmpty(currentPattern)) continue;
									item.ReplaceFilters.Add(new ReplaceFilter(currentPattern, value));
									break;
								}
						}
					}
				}
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message);

				item = new IdentifierItem();
			}
		}

		private static void Save(IdentifierItem item, out String xml)
		{
			try
			{
				var sb = new StringBuilder();
				sb.AppendLine(String.Format("CaseFilter:{0}", item.CaseFilter));
				sb.AppendLine(String.Format("IsAnInteger:{0}", item.IsAnInteger));
				sb.AppendLine(String.Format("IntegerLength:{0}", item.IntegerLength));
				foreach (var filter in item.ReplaceFilters)
				{
					sb.AppendLine(String.Format("ReplaceFilter.Pattern:{0}", filter.Pattern));
					sb.AppendLine(String.Format("ReplaceFilter.Replacement:{0}", filter.Replacement));
				}
				xml = sb.ToString();
			}
			catch (Exception)
			{
				xml = String.Empty;
			}
		}

		/// <summary>
		/// Notify that the SelectedIdentifiersReplaceFilters has been updated.
		/// </summary>
		/// <param name="sender">The sender of this event.</param>
		/// <param name="e">The event parameter.</param>
		private void Filter_PropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			NotifyPropertyChanged("SelectedIdentifiersReplaceFilters");
		}

		/// <summary>
		/// Raise the event PropertyChanged.
		/// </summary>
		/// <param name="info">The PropertyName.</param>
		private void NotifyPropertyChanged(String info)
		{
			if (PropertyChanged == null) return;
			PropertyChanged(this, new PropertyChangedEventArgs(info));
		}

		#endregion
	}
}